package scheduler

import (
	"encoding/json"
	"fmt"
	"gluenet/pkg/apis"
	"gluenet/pkg/meta"
	glue2 "gluenet/pkg/platform/glue"
	"gopkg.in/yaml.v3"
)

//目前是匹配第一个
func scheduleOne(agents []meta.Agent, instance *meta.Instance) (*meta.ScheduleResults, error) {
	if len(agents) == 0 {
		return nil, fmt.Errorf("no agent to schedule instance %v", instance)
	}

	middle1, err := buildMiddleResult(agents, instance)
	if err != nil {
		return nil, fmt.Errorf("build middle result %v", err)
	}

	middle2, err := filterAgentPlatform(middle1)
	if err != nil {
		return nil, fmt.Errorf("platfrom filter %v", err)
	}

	middle3, err := filterAgentNodeSelector(middle2)
	if err != nil {
		return nil, fmt.Errorf("node selector filter %v", err)
	}

	return selectorOne(middle3)
}

func buildMiddleResult(agents []meta.Agent, instance *meta.Instance) (*meta.ScheduleMiddleResults, error) {
	var (
		err error
		res = meta.ScheduleMiddleResults{}
	)
	//fmt.Printf("\n\n %v \n\n", *instance)
	switch instance.Platform {
	case apis.PlatfromKubernetes:
		res.Instances = instance
		res.InstancesCells = map[string]interface{}{instance.Guid: instance}
		res.ScheduleMap = map[string][]meta.Agent{
			instance.Guid: agents,
		}
		return &res, nil
	case apis.PlatfromGluenets:
		var gins glue2.Instance
		if err = json.Unmarshal([]byte(instance.InstanceValue), &gins); err != nil {
			return nil, err
		}
		var containers glue2.Containers

		//fmt.Printf("\n\n%v\n\n", gins.InstanceYaml)

		if err = yaml.Unmarshal([]byte(fmt.Sprintf("%v", gins.InstanceYaml)), &containers); err != nil {
			return nil, err
		}
		res.Instances = instance
		res.InstancesCells = make(map[string]interface{})
		res.ScheduleMap = make(map[string][]meta.Agent)
		for k, v := range containers.Containers {
			res.InstancesCells[k] = v
			res.ScheduleMap[k] = agents
		}
		return &res, nil
	default:
		return nil, fmt.Errorf("unknow platfrom %v", instance.Platform)
	}
}

func filterAgentPlatform(mid *meta.ScheduleMiddleResults) (*meta.ScheduleMiddleResults, error) {
	switch mid.Instances.Platform {
	case apis.PlatfromKubernetes:
		var ags []meta.Agent
		for _, ag := range mid.ScheduleMap[mid.Instances.Guid] {
			tags, err := meta.DeCodeTags(ag.Tags)
			if err != nil {
				return nil, err
			}

			if tags["platform"] == mid.Instances.Platform {
				ags = append(ags, ag)
			}
		}
		mid.ScheduleMap[mid.Instances.Guid] = ags
		return mid, nil
	case apis.PlatfromGluenets:
		for ck, _ := range mid.InstancesCells {
			var ags []meta.Agent
			for _, ag := range mid.ScheduleMap[ck] {
				tags, err := meta.DeCodeTags(ag.Tags)
				if err != nil {
					return nil, err
				}

				if tags["platform"] == mid.Instances.Platform {
					ags = append(ags, ag)
				}
			}
			mid.ScheduleMap[ck] = ags
		}
		return mid, nil
	default:
		return nil, fmt.Errorf("unknow platfrom %v", mid.Instances.Platform)
	}
}

func filterAgentNodeSelector(mid *meta.ScheduleMiddleResults) (*meta.ScheduleMiddleResults, error) {
	switch mid.Instances.Platform {
	case apis.PlatfromKubernetes:
		return mid, nil
	case apis.PlatfromGluenets:
		for ck, cell := range mid.InstancesCells {
			c, ok := cell.(glue2.Container)
			if !ok {
				return nil, fmt.Errorf("type cell is not Container, but %T", cell)
			}
			//tagc := glue.DeCodeScheduleLabels(c.ScheduleLabels)

			var ags []meta.Agent
			for _, ag := range mid.ScheduleMap[ck] {
				tags, err := meta.DeCodeTags(ag.Tags)
				if err != nil {
					return nil, err
				}

				match := true
				for k, v := range c.ScheduleLabels {
					if av, ok := tags[k]; !ok || av != v {
						match = false
					}
				}
				if match {
					ags = append(ags, ag)
				}
			}
			mid.ScheduleMap[ck] = ags
		}
		return mid, nil
	default:
		return nil, fmt.Errorf("unknow platfrom %v", mid.Instances.Platform)
	}
}

func selectorOne(mid *meta.ScheduleMiddleResults) (*meta.ScheduleResults, error) {
	var cells, maps, i = make([]string, len(mid.InstancesCells)), make(map[string]string, len(mid.InstancesCells)), 0
	for k, v := range mid.ScheduleMap {
		if len(v) == 0 {
			return nil, fmt.Errorf("instance cell %v can't find node", k)
		}
		cells[i] = k
		i++

		// todo select the first one
		maps[k] = v[0].Guid
	}

	return &meta.ScheduleResults{
		InstancesGuid:  mid.Instances.Guid,
		InstancesCells: meta.EnCodeScheduleCells(cells),
		ScheduleMap:    meta.EnCodeScheduleMaps(maps),
	}, nil
}
